require "sinatra/base"
require_relative "core_ext/string.rb"
require_relative "core_ext/regexp.rb"
require_relative "core_ext/array.rb"
require_relative "core_ext/symbol.rb"

module Sinatra

  # = Sinatra::FuzzyLayout
  #
  # <tt>Sinatra::FuzzyLayout</tt> is an extension to enable or disable
  # layouts for views with much more control. By default, regular Sinatra
  # provides two options:
  # 1. You can specify whether the layout should be enabled/disabled
  #    for all views.
  # 2. You can specify whether the layout should be enabled/disabled
  #    for an individual route/view.
  #
  # This extension provides two DSL methods `enable_layout_for` and
  # `disable_layout_for` which accept multiple parameters using which you
  # can have greater control over the layout-ing.
  #
  # == Usage
  #
  # In your application, you can use the `enable_layout_for` or
  # `disable_layout_for` DSL methods to specify which route/view should
  # have the layout and which routes/views should not.
  #
  #     enable_layout_for :index, :home, /user/
  #
  # All views that have the name "index", "home" or any view whose name
  # matches the regex /user/ will have it's layout disabled.
  #
  #     disable_layout_for /reports/
  #
  # The layout will be disabled for all views that match the regex /reports/
  # like "user_reports.slim" or "usage_reports.erb"
  #
  # === Classic Application
  #
  # To use the extension in a classic application, all you have to do is
  # require it.
  #
  #     require "sinatra"
  #     require "sinatra/fuzzy_layout"
  #
  #     enable_layout_for :index, :home, /user/
  #     disable_layout_for /reports/
  #
  #     # rest of the application goes here
  #
  # === Modular Application
  #
  # To use this in a modular application, you need to `register` the
  # extension like so:
  #
  #     require "sinatra/base"
  #     require "sinatra/fuzzy_layout"
  #
  #     class App < Sinatra::Base
  #       register Sinatra::FuzzyLayout
  #
  #       enable_layout_for :one, :two, :three
  #
  #       # rest of the application goes here
  #     end
  #
  module FuzzyLayout

    # This module is a placeholder for the non-DSL methods. To know more
    # about extending Sinatra or how the various bindings/encapsulation
    # works, refer to [this material on the topic](http://www.sinatrarb.com/extensions.html)
    #
    # This module gets included inside the Sinatra::Base class within the
    # call to `app.helpers TemplatesHelpers`. So we use the class hierarchy
    # to our advantage and override the `render` method to obtain the name
    # of the view and other options based on the name of the view.

    module TemplatesHelpers

      def render(engine, data, options = {}, locals = {}, &block)
        options = get_new_options_and_template(options, data)
        super
      end

      private


      # Sets the layout option for a particular view template.
      #
      # == Parameters
      # options::
      #   A hash of options generated by the app based on the settings
      #   by the user.
      #
      # template::
      #   A Symbol representing the name of the view. For example, `:index`

      def get_new_options_and_template(options, template)
        old_option = options.fetch(:layout) { false }
        if settings.enable_list.has_the_template?(template)
          options.merge!(:layout => (true && old_option))
        elsif settings.disable_list.has_the_template?(template)
          options.merge!(:layout => (false || old_option))
        end

        options
      end
    end

    def enable_layout_for(*templates)
      settings.enable_list.push(*templates)
    end

    def disable_layout_for(*templates)
      settings.disable_list.push(*templates)
    end

    def self.registered(app)
      app.set :enable_list, []
      app.set :disable_list, []
      app.helpers TemplatesHelpers
    end
  end

  register FuzzyLayout
end

